import asyncio
import hashlib
import re
from datetime import datetime
from typing import List

from fastapi import APIRouter, Depends, Query, Request, Form, Path
from fastapi.responses import HTMLResponse, RedirectResponse
from sqlalchemy import or_, select, update, delete, func, DateTime
from sse_starlette.sse import EventSourceResponse

from core.database import db_session
from core.exception import AlertException
from core.database import db_connect
from core.models import Group, Mail, Member
from core.template import AdminTemplates
from lib.common import get_admin_email, get_admin_email_name, select_query
from lib.dependency.dependencies import common_search_query_params, validate_token
from lib.mail import mailer
from lib.template_functions import get_group_select, get_paging

router = APIRouter()
templates = AdminTemplates()
templates.env.globals["get_group_select"] = get_group_select

MAIL_MENU_KEY = "200300"


@router.get("/mail_list")
async def mail_list(
    request: Request,
    db: db_session,
    search_params: dict = Depends(common_search_query_params)
):
    """
    会员邮件发货清单
    """
    request.session["menu_key"] = MAIL_MENU_KEY

    config = request.state.config

    result = select_query(
        request,
        db,
        Mail,
        search_params,
    )
    for i, mail in enumerate(result["rows"]):
        mail.num = result["total_count"] - (search_params["current_page"] - 1) * int(config.cf_page_rows) - i
        
    context = {
        "request": request,
        "config": config,
        "member": request.state.login_member,
        "total_count": result["total_count"],
        "mails": result["rows"],
        "paging": get_paging(request, search_params["current_page"], result["total_count"]),
    }
    return templates.TemplateResponse("mail_list.html", context)


# @router.get("/mail_form")
# async def mail_form(request: Request, db: db_session,):
#     """
#     注册发送会员邮件
#     """
#     request.session["menu_key"] = "200300"

#     context = {
#         "request": request,
#         "config": request.state.config,
#         "member": request.state.login_member,
#         "mail": None,
#     }
#     return templates.TemplateResponse("mail_form.html", context)


@router.get("/mail_form")  # 注册
@router.get("/mail_form/{ma_id}")  # 校正
async def mail_form(
    request: Request,
    db: db_session,
    ma_id: int = None
):
    """
    注册并正确发送会员邮件
    - ma_id 如果没有注册, 如果有
    """
    request.session["menu_key"] = MAIL_MENU_KEY

    mail = db.get(Mail, ma_id)
    # if not mail:
    #     raise AlertException("无邮件信息.", 400)

    context = {
        "request": request,
        "config": request.state.config,
        "member": request.state.login_member,
        "mail": mail,
    }
    return templates.TemplateResponse("mail_form.html", context)


@router.post("/mail_update", dependencies=[Depends(validate_token)])
async def mail_form_update(
    request: Request,
    db: db_session,
    ma_id: int = Form(None),
    ma_subject: str = Form(...),
    ma_content: str = Form(...),
):
    """
    注册发送会员邮件/校正
    """
    # 注册
    if not ma_id:
        mail = Mail(
            ma_subject=ma_subject,
            ma_content=ma_content,
            ma_time=datetime.now(),
            ma_ip=request.client.host
        )
        db.add(mail)
        db.commit()
        ma_id = mail.ma_id
    else:  # 校正
        mail = db.get(Mail, ma_id)
        if not mail:
            raise AlertException("无邮件信息.", 400)

        mail.ma_subject = ma_subject
        mail.ma_content = ma_content
        db.commit()

    return RedirectResponse(f"/admin/mail_form/{ma_id}", status_code=303)


@router.post("/mail_delete", dependencies=[Depends(validate_token)])
async def mail_delete(
    request: Request,
    db: db_session,
    checks: List[int] = Form(..., alias="chk[]"),
    ma_id: List[int] = Form(..., alias="ma_id[]"),
):
    """
    删除会员邮件
    """
    for i in checks:
        db.execute(delete(Mail).where(Mail.ma_id == ma_id[i]))
    db.commit()

    return RedirectResponse("/admin/mail_list", status_code=303)


@router.get("/mail_test/{ma_id}")
async def mail_test(
    request: Request,
    db: db_session,
    ma_id: int = Path(...),
):
    """
    会员邮件发送测试
    """
    config = request.state.config
    if not config.cf_email_use:
        raise AlertException("在环境设置中 '使用邮件发送'你需要检查发送邮件.", 400)

    exists_mail = db.get(Mail, ma_id)
    if not exists_mail:
        raise AlertException("无邮件信息.", 400)

    login_member = request.state.login_member
    name = login_member.mb_name
    nick = login_member.mb_nick
    mb_id = login_member.mb_id
    email = login_member.mb_email
    ma_id = exists_mail.ma_id

    subject = exists_mail.ma_subject
    content = exists_mail.ma_content
    # 被取代
    content = content.replace("{姓名}", name)
    content = content.replace("{昵称}", nick)
    content = content.replace("{会员Id}", mb_id)
    content = content.replace("{电子邮件}", email)

    mb_md5 = hashlib.md5(f"{mb_id}{email}{login_member.mb_datetime}".encode()).hexdigest()
    content = content + f'<p>如果你不想收到更多的信息， [<a href="/bbs/email_stop/{mb_id}&mb_md5={mb_md5}" target="_blank">拒绝接收</a>] 请这样做。.</p>' 

    # 已邮寄
    mailer(get_admin_email(request), email, subject, content, get_admin_email_name(request))

    raise AlertException(f"{nick}({email})我们给你发了一封测试邮件. 请确认.")


@router.get("/mail_select_form/{ma_id}")
async def mail_select_form(
    request: Request,
    db: db_session,
    ma_id: int = Path(...),
    mb_id1: int = Query(1),
    mb_level_from: str = Query(1),
    mb_level_to: int = Query(10),
    mb_mailling: bool = Query(1),
    mb_email: str = Query(""),
):
    """
    会员邮件选择
    """
    request.session["menu_key"] = MAIL_MENU_KEY

    config = request.state.config
    if not config.cf_email_use:
        raise AlertException("在环境设置中 '使用邮件发送'你需要检查发送邮件.", 403)

    exists_mail = db.get(Mail, ma_id)
    if not exists_mail:
        raise AlertException("无邮件信息.")

    cleaned_host = re.sub(r'^(www[^\.]*\.)', '', request.client.host)

    groups = db.scalars(select(Group).order_by(Group.gr_subject)).all()

    # 已满/分裂国家成员的数量
    member_count = db.scalar(select(func.count(Member.mb_id)))
    leave_count = db.scalar(
        select(func.count(Member.mb_id))
        .where(Member.mb_leave_date != "")
    )

    context = {
        "request": request,
        "config": config,
        "member": request.state.login_member,
        "mail": exists_mail,
        "cleaned_host": cleaned_host,
        "groups": groups,
        "mb_id1": mb_id1,
        "mb_level_from": mb_level_from,
        "mb_level_to": mb_level_to,
        "mb_mailling": mb_mailling,
        "mb_email": mb_email,
        "member_count": member_count,
        "leave_count": leave_count,
    }
    return templates.TemplateResponse("mail_select_form.html", context)


@router.post("/mail_select_list", dependencies=[Depends(validate_token)])
async def mail_select_list(
    request: Request,
    db: db_session,
    ma_id: int = Form(..., alias="ma_id"),
    mb_id1: int = Form(None, alias="mb_id1"),
    mb_id1_from: str = Form(None, alias="mb_id1_from"),
    mb_id1_to: str = Form(None, alias="mb_id1_to"),
    mb_email: str = Form(None, alias="mb_email"),
    mb_mailling: str = Form(None, alias="mb_mailling"),
    mb_level_from: int = Form(None, alias="mb_level_from"),
    mb_level_to: int = Form(None, alias="mb_level_to"),
    gr_id: str = Form(None, alias="gr_id"),
):
    """
    会员邮件选择
    """
    query = select(Member).where(Member.mb_level.between(mb_level_from, mb_level_to))

    if mb_id1 != 1:
        query = query.where(Member.mb_id.between(mb_id1_from, mb_id1_to))
    if mb_email:
        query = query.where(Member.mb_email.like(f"%{mb_email}%"))
    if mb_mailling:
        query = query.where(Member.mb_mailling == mb_mailling)

    if gr_id:
        group_members = db.get(Group, gr_id).members
        if not group_members:
            raise AlertException("不是所选公告板组的一个成员.")
        group_member_ids = [member.mb_id for member in group_members]
        query = query.where(Member.mb_id.in_(group_member_ids))

    # 撤回, 仅选择尚未阻止的成员.
    # 1年 1月份 1工作的 datetime 我们创建一个对象.
    cutoff_date = datetime(1, 1, 1)
    # cutoff_date 之前 mb_leave_date和 mb_intercept_date仅选择具有以下特征的成员.
    current_db_engine = db_connect._db_engine
    if current_db_engine == 'sqlite':
        comparing_leave_date = Member.mb_leave_date <= cutoff_date
        comparing_intercept_date = Member.mb_intercept_date <= cutoff_date
    else:
        comparing_leave_date = func.cast(Member.mb_leave_date, DateTime) <= cutoff_date
        comparing_intercept_date = func.cast(Member.mb_intercept_date, DateTime) <= cutoff_date            
    query = query.where(
        or_(Member.mb_leave_date == "", comparing_leave_date),
        or_(Member.mb_intercept_date == "", comparing_intercept_date)
    )
    members = db.scalars(query).all()

    # members 中国 ma_last_option 保存在字段中 (파이썬, PHP의 차이점으로 인해 POST로 넘기지 못하고 DB에 저장해야함)
    # members 를 ma_last_option 필드에 저장함 (python, PHP由于差异 POST我不能把它翻过来。 DB必须存储在)
    save_members = []
    textarea_members = []
    for member in members:
        save_members.append(
            member.mb_name
            + "||" + member.mb_nick
            + "||" + member.mb_id
            + "||" + member.mb_email
        )
        textarea_members.append(
            member.mb_email
            + "||" + member.mb_id
            + "||" + member.mb_name
            + "||" + member.mb_nick
            + "||" + member.mb_datetime.strftime("%Y-%m-%d %H:%M:%S")
        )
    save_members_str = "\n".join(save_members)
    textarea_members_str = "\n".join(textarea_members)

    db.execute(
        update(Mail)
        .where(Mail.ma_id == ma_id)
        .values(ma_last_option=save_members_str)
    )
    db.commit()

    extend = {
        "request": request,
        "config": request.state.config,
        "login_member": request.state.login_member,
        "members": members,
        "ma_id": ma_id,
        "textarea_members_str": textarea_members_str,
    }
    return templates.TemplateResponse("mail_select_list.html", extend)


@router.post("/mail_select_result", dependencies=[Depends(validate_token)], response_class=HTMLResponse)
async def mail_select_result(
    request: Request,
    db: db_session,
    ma_id: int = Form(...),
):
    """
    显示结果的会员邮件 HTML 页数
    """
    context = {
        "request": request,
        "ma_id": ma_id,
    }
    return templates.TemplateResponse("mail_select_result.html", context)


@router.get("/mail_select_send")
async def mail_select_send(
    request: Request,
    db: db_session,
    ma_id: int = Query(...),
):
    from_mail = get_admin_email(request)
    from_name = get_admin_email_name(request)
    """
    会员邮件发送处理
    """
    async def send_events(members: list, mail_subject: str, mail_content: str):
        count = 0
        sleepsec = 1  # 1以秒为间隔进行调整
        try:
            for member in members:
                mb_name, mb_nick, mb_id, mb_email = member.split("||")

                if not mb_email:
                    continue
                
                mb_md5 = hashlib.md5(f"{mb_id}{mb_email}{datetime.now()}".encode()).hexdigest()

                subject = mail_subject
                content = mail_content
                content = content.replace("{姓名}", mb_name)
                content = content.replace("{昵称}", mb_nick)
                content = content.replace("{会员Id}", mb_id)
                content = content.replace("{电子邮件}", mb_email)
                content = content + f"<hr size=0><p><span style='font-size:10pt; font-family:发芽'>▶ 如果你不想收到更多的信息， [<a href='/bbs/email_stop/{mb_id}&mb_md5={mb_md5}' target='_blank'>拒绝接收</a>] 请这样做。.</span></p>"           
                
                # 已邮寄
                mailer(from_mail, mb_email, subject, content, from_name)
                count += 1

                # 10对于每一个男人 1我们给你第二次休息。.
                if count % 10 == 0:
                    await asyncio.sleep(sleepsec)  # 无动力 sleep 使用

                # 发货状态 'yield'用于转移.
                # 在传输的情况下 data: 并开始与 \n\n它应该以.
                yield f"data: {count}. {mb_name}({mb_email})我在给你发邮件。.\n\n"

            # 发送会员列表邮件后结束功能
            # 结束消息传输
            yield "data: [结束]\n\n"
        except Exception as e:
            yield f"data: [错误] {e}\n\n"

    async def send_error_events(message: str):
        yield "data: 发送邮件时出错.\n\n"
        yield f"[错误] {message}\n\n"

    exists_mail = db.get(Mail, ma_id)
    if not exists_mail.ma_subject or not exists_mail.ma_content:
        return EventSourceResponse(send_error_events("无邮件内容."))

    members = exists_mail.ma_last_option.split("\n")

    return EventSourceResponse(send_events(members, exists_mail.ma_subject, exists_mail.ma_content))


@router.get("/mail_preview/{ma_id}")
async def mail_preview(
    request: Request,
    db: db_session,
    ma_id: int = Path(...),
):
    """
    会员邮件发送预视
    """
    request.session["menu_key"] = MAIL_MENU_KEY

    exists_mail = db.get(Mail, ma_id)
    if not exists_mail:
        raise AlertException("无邮件信息.", 400)

    login_member = request.state.login_member

    subject = exists_mail.ma_subject
    content = exists_mail.ma_content

    content = content.replace("{姓名}", login_member.mb_name)
    content = content.replace("{昵称}", login_member.mb_nick)
    content = content.replace("{会员Id}", login_member.mb_id)
    content = content.replace("{电子邮件}", login_member.mb_email)

    content = content + f"<hr size=0><p><span style='font-size:10pt; font-family:发芽'>\
    ▶ 如果你不想收到更多的信息，\
    [<a href='/bbs/email_stop/{login_member.mb_id}&mb_md5=***' target='_blank'>拒绝接收</a>]\
    请这样做。.</span></p>"

    context = {
        "request": request,
        "mail_subject": subject,
        "mail_content": content,
    }
    return templates.TemplateResponse("mail_preview.html", context)
